﻿using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Threading;
using Timer = System.Windows.Forms.Timer;

namespace HookedOn
{
    public partial class HookedOn : Form
    {
        public delegate void MyDelegate();
        //MyDelegate delInstance = new MyDelegate(MyFunction);

        #region Keyhandler

        public static class Constants
        {
            //windows message id for hotkey
            public const int WM_HOTKEY_MSG_ID = 0x0312;
        }

        public class KeyHandler
        {
            [DllImport("user32.dll")]
            private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);

            [DllImport("user32.dll")]
            private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

            private int key;
            private IntPtr hWnd;
            private int id;

            public KeyHandler(Keys key, Form form)
            {
                this.key = (int)key;
                this.hWnd = form.Handle;
                id = this.GetHashCode();
            }

            public override int GetHashCode()
            {
                return key ^ hWnd.ToInt32();
            }

            public bool Register()
            {
                return RegisterHotKey(hWnd, id, 0, key);
            }

            public bool Unregiser()
            {
                return UnregisterHotKey(hWnd, id);
            }
        }

        #endregion

        #region Perform Mouse click

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);

        private const int MOUSEEVENTF_LEFTDOWN = 0x02;
        private const int MOUSEEVENTF_LEFTUP = 0x04;
        private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
        private const int MOUSEEVENTF_RIGHTUP = 0x10;

        #endregion

        readonly Timer timer = new Timer();

        public HookedOn()
        {
            InitializeComponent();

            // Everytime timer ticks, timer_Tick will be called
            timer.Tick += timer_Tick;
            // Timer will tick per second
            timer.Interval = (1000) * (1);
        }

        #region Member variables

        private KeyHandler _ghk;
        /// <summary>
        /// _listPosition[0,] tha can
        /// _listPosition[1,] thu can
        /// _listPosition[2,] nhat do
        /// _listPosition[3,] hot spot
        /// </summary>
        private int[,] _listPosition = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
        private int _flag;
        private bool _continue = false;
        private readonly string _path = string.Concat(Environment.CurrentDirectory, @"\save.dat");
        private string _color = "";
        private string _msg = "";
        private int _count;

        #endregion

        #region Ultilities

        #region GetPixelColor

        [DllImport("user32.dll")]
        static extern IntPtr GetDC(IntPtr hwnd);

        [DllImport("user32.dll")]
        static extern Int32 ReleaseDC(IntPtr hwnd, IntPtr hdc);

        [DllImport("gdi32.dll")]
        static extern uint GetPixel(IntPtr hdc, int nXPos, int nYPos);

        static public Color GetPixelColor(int x, int y)
        {
            IntPtr hdc = GetDC(IntPtr.Zero);
            uint pixel = GetPixel(hdc, x, y);
            ReleaseDC(IntPtr.Zero, hdc);
            Color color = Color.FromArgb((int)(pixel & 0x000000FF),
                         (int)(pixel & 0x0000FF00) >> 8,
                         (int)(pixel & 0x00FF0000) >> 16);
            return color;
        }

        #endregion

        /// <summary>
        /// what to do when registered key is pressed
        /// </summary>
        private void HandleHotkey()
        {
            _listPosition[_flag, 0] = Cursor.Position.X;
            _listPosition[_flag, 1] = Cursor.Position.Y;

            string msg = "Position " + (_flag + 1) + ": " + _listPosition[_flag, 0] + "-" + _listPosition[_flag, 1];
            AddText(msg);

            _flag++;
            if (_flag == 4)
            {
                //// Restore windows
                //WindowState = FormWindowState.Normal;
                // Uninstall ..
                _ghk.Unregiser();
                // Add notification to Messages text box
                msg = "Unregistered.";
                AddText(msg);
                msg = "Get coordinates done.";
                AddText(msg);
            }
        }

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == Constants.WM_HOTKEY_MSG_ID)
                HandleHotkey();
            base.WndProc(ref m);
        }

        private void AddText(string message)
        {
            if (message == null)
            {
                return;
            }

            int length = tbMessages.Text.Length + message.Length;
            if (length >= tbMessages.MaxLength)
            {
                tbMessages.Text = "";
            }

            if (!message.EndsWith("\r\n"))
            {
                message += "\r\n";
            }

            tbMessages.Text = message + tbMessages.Text;
        }

        /// <summary>
        /// Perform mouse click on given position
        /// </summary>
        public void DoMouseClick(int x, int y)
        {
            // Move mouse
            Cursor.Position = new Point(x, y);
            // Perform click
            mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, x, y, 0, 0);
        }

        /// <summary>
        /// Everytime timer ticks, timer_Tick will be called
        /// </summary>
        void timer_Tick(object sender, EventArgs e)
        {
            if (lbStatus.Text.Length > 12)
            {
                lbStatus.Text = "Fishing ";
            }
            lbStatus.Text = lbStatus.Text + @".";
        }

        #endregion

        /// <summary>
        /// Start monitor a key
        /// </summary>
        private void btInstall_Click(object sender, EventArgs e)
        {
            // Install ..
            _ghk = new KeyHandler(Keys.G, this);
            _ghk.Register();
            _flag = 0;
            // Add notification to Messages text box
            string msg = "Registered key: " + Keys.G;
            AddText(msg);
            //// Minimize windows
            //WindowState = FormWindowState.Minimized;
        }

        /// <summary>
        /// Stop auto hooking
        /// </summary>
        private void btFinish_Click(object sender, EventArgs e)
        {
            // TODO
            _continue = false;
        }

        /// <summary>
        /// Check Always on top Option
        /// </summary>
        private void cbTopMost_CheckStateChanged(object sender, EventArgs e)
        {
            if (cbTopMost.Checked)
            {
                TopMost = true;
            }
            else
            {
                TopMost = false;
            }
        }

        /// <summary>
        /// Start auto hooking
        /// </summary>
        private void btHooked_Click(object sender, EventArgs e)
        {
            try
            {
                Thread t = new Thread(performFishing);
                // quit auto run
                if (_continue)
                {
                    _continue = false;
                    // Terminate thread
                    t.Abort();
                    // Erase status bar
                    lbStatus.Text = "     ";
                    // Stop timer
                    timer.Stop();
                    _msg = "Stopped.";
                    AddText(_msg);
                    _msg = "Total hook: " + _count;
                    AddText(_msg);
                    return;
                }
                // first run, turn on auto run
                _continue = true;
                _count = 0;

                // check if all coordinates are availables
                bool ready = true;
                foreach (var i in _listPosition)
                {
                    if (i == 0)
                    {
                        ready = false;
                        _msg = "Coordinate not found";
                        AddText(_msg);
                        _msg = "Read from save file";
                        AddText(_msg);
                        break;
                    }
                }
                if (!ready)
                {
                    // try to read from save file.
                    if (!File.Exists(_path))
                    {
                        //file not existed, return
                        _msg = "Save file not found!";
                        AddText(_msg);
                        return;
                    }

                    // read file
                    string[] lines = File.ReadAllLines(_path);
                    if (lines.Length < 5)
                    {
                        // not the correct file
                        _msg = "Wrong save file!";
                        AddText(_msg);
                        return;
                    }
                    for (int j = 0; j < 5; j++)
                    {
                        // get color
                        if (j == 4)
                        {
                            _color = lines[j];
                            continue;
                        }
                        // get mouse coordinates
                        string[] temp = lines[j].Split('-');
                        int dump1, dump2;
                        Int32.TryParse(temp[0], out dump1);
                        Int32.TryParse(temp[1], out dump2);
                        _listPosition[j, 0] = dump1;
                        _listPosition[j, 1] = dump2;
                    }
                    _msg = "Successed.";
                    AddText(_msg);
                }

                // Start auto fishing
                _msg = "Start fishsing..";
                AddText(_msg);

                lbStatus.Text = "Fishing ";
                // Enable the timer
                timer.Enabled = true;
                // Start the timer
                timer.Start();

                // Start thread
                t.Start();
            }
            catch (Exception exception)
            {
                _msg = exception.Message;
                AddText(_msg);
            }
        }

        /// <summary>
        /// Show about dialog
        /// </summary>
        private void btAbout_Click(object sender, EventArgs e)
        {
            var aboutForm = new About { TopMost = false };
            aboutForm.Show(this);
        }

        /// <summary>
        /// Save mouse position to save file
        /// </summary>
        private void btSave_Click(object sender, EventArgs e)
        {
            // 0 stand for no input => do nothing
            foreach (var i in _listPosition)
            {
                if (i == 0)
                {
                    return;
                }
            }
            if (string.IsNullOrEmpty(_color))
                return;
            // try to save to file
            try
            {
                // Save to new save file, if existed, overwrite
                File.WriteAllText(_path, _listPosition[0, 0] + "-" + _listPosition[0, 1]);
                // Append to save file
                using (var file = new StreamWriter(_path, true))
                {
                    file.WriteLine("");
                    file.WriteLine(_listPosition[1, 0] + "-" + _listPosition[1, 1]);
                    file.WriteLine(_listPosition[2, 0] + "-" + _listPosition[2, 1]);
                    file.WriteLine(_listPosition[3, 0] + "-" + _listPosition[3, 1]);
                    file.WriteLine(_color);
                }
                string msg = "Saved.";
                AddText(msg);
            }
            catch (Exception exception)
            {
                _msg = exception.Message;
                AddText(_msg);
            }
        }

        /// <summary>
        /// Get color from the hot spot
        /// </summary>
        private void btColor_Click(object sender, EventArgs e)
        {
            _color = GetPixelColor(_listPosition[3, 0], _listPosition[3, 1]).Name;
            string msg = "Get color done.";
            AddText(msg);
        }

        /// <summary>
        /// Perform auto fishing, run from different thread
        /// </summary>
        private void performFishing()
        {
            while (_continue)
            {
                DoMouseClick(_listPosition[0, 0], _listPosition[0, 1]);
                Thread.Sleep(2000);
                while (_continue && GetPixelColor(_listPosition[3, 0], _listPosition[3, 1]).Name != _color)
                {
                    // TODO wait..
                }
                if (_continue)
                {
                    DoMouseClick(_listPosition[1, 0], _listPosition[1, 1]);
                    Thread.Sleep(3000);
                    DoMouseClick(_listPosition[2, 0], _listPosition[2, 1]);
                    Thread.Sleep(1000);
                    _count++;
                }
            }
        }

    }
}
